iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 28
0
自我挑戰組

golang初探系列 第 28

day28-Context介紹

  • 分享至 

  • xImage
  •  

Go 中的 goroutine 之間沒有父與子的關係,也就沒有子進程退出後的通知機制,goroutine 都是平行的被調度。
在開發的過程當中,可能會開多個 goroutine 來操作,今天議題是如何優雅讓子 goroutine 優雅退出

exampel

使用select + chan 方式溝通
但試想一個場景是主 goroutine 當中,又啟用子 goroutine,當主 gorutine 結束後要通知子 gorutine
這時要討論到 Context
Go 1.7 加入了新個package 為 context ,定義了 Context 類型,專用來簡化處理多個 goroutine 之間請求領域的數據、取消信號、截止時間等相關操作

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func worker(closeChan chan struct{}) {
LOOP:
	for {
		select {
		case <-closeChan:
			break LOOP
		default:
			fmt.Println("still  worker")
			time.Sleep(time.Second)

		}
	}
	wg.Done()
}

func main() {
	var closeChan = make(chan struct{})
	wg.Add(1)
	go worker(closeChan)
	time.Sleep(time.Second * 3)
	closeChan <- struct{}{}
	close(closeChan)
	wg.Wait()
	fmt.Println("close")
}


Context

Context 為一個 interface 定義了四個需要實現的方法,具體如下

  • Deadline() 返回當前 Context 被取消的時間,也就是完成的截止時間
  • Done() 當前工作完成後或上下文被取消之後進行返回一個 chan
  • Err() 返回錯誤訊息(如果是關閉 返回a non-nil error,取消則返回 Canceled error,若是過期則返回 DeadlineExceeded)
  • Value 返回和 context 相關連的值

Background() 和 TODO()

Go内置兩個函数可以創建 Context:
Background() 和 TODO(),分别返回一個實現了Context接口的 background 和 todo。

  • Background() 主要用於 main func、初始化以及測試代碼,作為 Context 这個樹結構树結構的最頂層的 Context,也就是 Root Context。
  • TODO() 當不清楚該使用那一個 Context的時候,可以使用 context.TODO。

WithCancel

使用 WithCancel() 將傳入 context 進行複製返回 cancelCtx 和 CancelFunc
若 CancelFunc 被執行時,會觸發去執行通知每個 cancelCtx children 進行cancel()
詳見源碼中的406行

example
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func work2(ctx context.Context) {
	defer wg.Done()
LOOP:
	for {

		select {
		case <-ctx.Done():

			break LOOP
		default:
			time.Sleep(1 * time.Second)
			fmt.Println("work2 is working")
		}
	}
}

func work1(ctx context.Context) {
	defer wg.Done()
LOOP:
	for {
		go work2(ctx)
		select {
		case <-ctx.Done():
			break LOOP
		default:
			time.Sleep(1 * time.Second)
			fmt.Println("work1 is working")

		}
	}
}


func main() {

	ctx, cancel := context.WithCancel(context.Background())

	wg.Add(1)
	go work1(ctx)
	time.Sleep(3 * time.Second)
	cancel()

	wg.Wait()

}

WithDeadline

將傳入的parent Context進行複製並將傳入的時間進行調整為截止時間,當時間到截止時間則會進行關閉,也可以就由返回的 CancelFunc 進行關閉

example

設置過期時間 1 秒後過期

package main

import (
	"context"
	"fmt"
	"sync"

	"time"
)

var wg sync.WaitGroup

func worker(ctx context.Context) {
LOOP:
	for {

		select {
		case <-ctx.Done():
			break LOOP
		default:
			time.Sleep(time.Millisecond * 100)
			fmt.Println("still working")
		}
	}
	fmt.Println("worker done!")
	wg.Done()
}

func main() {

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
	wg.Add(1)
	go worker(ctx)
	time.Sleep(time.Second * 5)
	cancel()
	wg.Wait()
	fmt.Println("over")
}

WithValue

將傳入的parent Context進行複製並將傳入的 key 及 value 進行關聯
由Context.Value() 獲取 value

example

package main

import (
	"context"
	"fmt"
)

func main() {
	type favContextKey string

	f := func(ctx context.Context, k favContextKey) {
		if v := ctx.Value(k); v != nil {
			fmt.Println("found value:", v)
			return
		}
		fmt.Println("key not found:", k)
	}

	k := favContextKey("language")
	ctx := context.WithValue(context.Background(), k, "Go")

	f(ctx, k)
	f(ctx, favContextKey("color"))

}

上一篇
day27-Data Race
下一篇
day29- Testing
系列文
golang初探30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言